﻿using Microsoft.EntityFrameworkCore;
using Surpass.Common.CustomException;
using Surpass.Model;
using Surpass.Model.BaseDomain;
using Surpass.Model.Dto.ResponseDto.ResponseModel.ResultModel;
using Surpass.Repository.IRepository;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace Surpass.Repository.Repository
{
    public abstract class DataBase : IDataBase
    {
        private readonly SurpassDbContext _surpassDbContext;
        public DataBase(SurpassDbContext surpassDbContext)
        {
            _surpassDbContext = surpassDbContext;
        }
        //建议查询方法加上数据库跟踪
        public void CloseTracking()
        {
            _surpassDbContext.ChangeTracker.QueryTrackingBehavior = Microsoft.EntityFrameworkCore.QueryTrackingBehavior.NoTracking;
        }

        public T Add<T>(T entity) where T : BaseModel, new()
        {
            _surpassDbContext.Set<T>().Add(entity);
            
            return entity;

        }

        public List<T> Add<T>(List<T> entity) where T : BaseModel, new()
        {
            _surpassDbContext.Set<T>().AddRange(entity);
            
            return entity;
        }

        public async Task<T> AddAsync<T>(T entity) where T : BaseModel, new()
        {
            await _surpassDbContext.Set<T>().AddAsync(entity);
            return entity;
        }

        public async Task<List<T>> AddAsync<T>(List<T> entity) where T : BaseModel, new()
        {
            await _surpassDbContext.Set<T>().AddRangeAsync(entity);
            return entity;
        }

        public int Count<T>(Expression<Func<T, bool>> countLambda) where T : BaseModel, new()
        {
            return _surpassDbContext.Set<T>().AsNoTracking().Count(countLambda);
        }

        public async Task<int> CountAsync<T>(Expression<Func<T, bool>> countLambda) where T : BaseModel, new()
        {
            return await _surpassDbContext.Set<T>().AsNoTracking().CountAsync(countLambda);
        }

        public void Delete<T>(params object[] keyValues) where T : BaseModel, new()
        {
            var entity = _surpassDbContext.Set<T>().Find(keyValues);
            _surpassDbContext.Entry(entity).State = EntityState.Deleted;
        }

        public async Task DeleteAsync<T>(params object[] keyValues) where T : BaseModel, new()
        {
            var entity = await _surpassDbContext.Set<T>().FindAsync(keyValues);
             _surpassDbContext.Entry(entity).State = EntityState.Deleted;
        }

        public bool Exist<T>(Expression<Func<T, bool>> anyLambda) where T : BaseModel, new()
        {
            return _surpassDbContext.Set<T>().Any(anyLambda);
        }

        public Task<bool> ExistAsync<T>(Expression<Func<T, bool>> anyLambda) where T : BaseModel, new()
        {
            return _surpassDbContext.Set<T>().AnyAsync(anyLambda);
        }

        public T Find<T>(params object[] keyValues) where T : BaseModel, new()
        {
            return _surpassDbContext.Set<T>().Find(keyValues);
        }

        public async Task<T> FindAsync<T>(params object[] keyValues) where T : BaseModel, new()
        {
            return await _surpassDbContext.Set<T>().FindAsync(keyValues);
        }

        public T First<T>(Expression<Func<T, bool>> firstLambda) where T : BaseModel, new()
        {
            return _surpassDbContext.Set<T>().FirstOrDefault(firstLambda);
        }

        public async Task<T> FirstAsync<T>(Expression<Func<T, bool>> firstLambda) where T : BaseModel, new()
        {
            return await _surpassDbContext.Set<T>().FirstOrDefaultAsync(firstLambda);
        }

        public T FirstOrderByDesc<T, TOrderKey>(Expression<Func<T, bool>> match, Expression<Func<T, TOrderKey>> orderFun) where T : BaseModel, new()
        {
            return _surpassDbContext.Set<T>().Where(match).OrderByDescending(orderFun).FirstOrDefault();
        }

        public async Task<T> FirstOrderByDescAsync<T, TOrderKey>(Expression<Func<T, bool>> match, Expression<Func<T, TOrderKey>> orderFun) where T : BaseModel, new()
        {
            return await _surpassDbContext.Set<T>().Where(match).OrderByDescending(orderFun).FirstOrDefaultAsync();
        }

        public IQueryable<T> LoadEntities<T>(Expression<Func<T, bool>> whereLambda = null) where T : BaseModel, new()
        {
            if (whereLambda==null)
            {
                return _surpassDbContext.Set<T>().AsNoTracking().Where(u => true);
            }
            return _surpassDbContext.Set<T>().AsNoTracking().Where(whereLambda);
        }

        public async Task<List<T>> LoadEntitiesAsync<T>(Expression<Func<T, bool>> whereLambda = null) where T : BaseModel, new()
        {
            if (whereLambda == null)
            {
                return await _surpassDbContext.Set<T>().AsNoTracking().Where(u => true).ToListAsync();
            }
            return await _surpassDbContext.Set<T>().AsNoTracking().Where(whereLambda).ToListAsync();
        }

        public List<T> LoadPageEntities<T, TKey>(int pageIndex, int pageSize, out int totalCount, out int pageCount, Expression<Func<T, bool>> whereLambda, bool isAsc, Expression<Func<T, TKey>> orderBy) where T : BaseModel, new()
        {
            pageIndex = pageIndex < 1 ? 1 : pageIndex;
            pageSize = pageSize < 1 ? 20 : pageSize;
            var temp = _surpassDbContext.Set<T>().AsNoTracking().Where(whereLambda);
            totalCount = temp.Count();
            pageCount = Convert.ToInt32(Math.Ceiling(totalCount / 1.0 * pageSize));
            if (isAsc)
            {
                return temp.OrderBy(orderBy)
                    .Skip((pageIndex - 1) * pageSize)
                    .Take(pageSize)
                    .Select(u => new T())
                    .ToList();
            }
            return temp.OrderByDescending(orderBy)
                    .Skip((pageIndex - 1) * pageSize)
                    .Take(pageSize)
                    .Select(u => new T())
                    .ToList();
        }

        public PageResponse<T> LoadPageEntities<T, TKey>(int pageIndex, int pageSize, Expression<Func<T, bool>> whereLambda, bool isAsc, Expression<Func<T, TKey>> orderBy) where T : BaseModel, new()
        {
            pageIndex = pageIndex < 1 ? 1 : pageIndex;
            pageSize = pageSize < 1 ? 20 : pageSize;
            var temp = _surpassDbContext.Set<T>().AsNoTracking().Where(whereLambda); //去掉.AsQueryable().AsNoTracking()，将下面改为

            var rest = new PageResponse<T>();
            rest.CurrentPage = pageIndex;
            rest.PageSize = pageSize;
            rest.RecordTotal = temp.Count();//记录总条数时，自动设置了总页数
            if (isAsc)
            {
                rest.PageData = temp.OrderBy(orderBy)
                     .Skip(pageSize * (pageIndex - 1))
                     .Take(pageSize).Select(t => new T()).ToList(); //去掉.AsQueryable()，添加.select(t=>new Dto()).ToList()
            }

            rest.PageData = temp.OrderByDescending(orderBy)
                .Skip(pageSize * (pageIndex - 1))
                .Take(pageSize).Select(t => new T()).ToList(); //.select(t=>new Dto()).ToList()

            return rest;
        }

        public IQueryable<T> LoadPageEntities<T, TKey>(IQueryable<T> query, int pageIndex, int pageSize, out int totalCount, out int pageCount, bool isAsc, Expression<Func<T, TKey>> orderBy) where T : class, new()
        {
            pageIndex = pageIndex < 1 ? 1 : pageIndex;
            pageSize = pageSize < 1 ? 20 : pageSize;
            IQueryable<T> temp = query.AsNoTracking();
            totalCount = temp.Count();
            pageCount = (int)Math.Ceiling((double)totalCount / pageSize);
            if (isAsc)
            {
                temp = temp.OrderBy(orderBy)
                           .Skip(pageSize * (pageIndex - 1))
                           .Take(pageSize).AsQueryable();
            }
            else
            {
                temp = temp.OrderByDescending(orderBy)
                          .Skip(pageSize * (pageIndex - 1))
                          .Take(pageSize).AsQueryable();
            }
            return temp;
        }

        public PageResponse<T> LoadPageEntities<T, TKey>(IQueryable<T> query, int pageIndex, int pageSize, bool isAsc, Expression<Func<T, TKey>> orderBy) where T : class, new()
        {
            pageIndex = pageIndex < 1 ? 1 : pageIndex;
            pageSize = pageSize < 1 ? 20 : pageSize;
            var rest = new PageResponse<T>();
            IQueryable<T> temp = query.AsNoTracking();
            rest.PageSize = pageSize;
            rest.CurrentPage = pageIndex;
            rest.RecordTotal = temp.Count();
            if (isAsc)
            {
                rest.PageData = temp.OrderBy(orderBy)
                    .Skip(pageSize * (pageIndex - 1))
                    .Take(pageSize).ToList();
            }
            else
            {
                rest.PageData = temp.OrderByDescending(orderBy)
                    .Skip(pageSize * (pageIndex - 1))
                    .Take(pageSize).ToList();
            }
            return rest;
        }

        public async Task<PageResponse<T>> LoadPageEntitiesAsync<T, TKey>(int currentPage, int pageSize, Expression<Func<T, bool>> whereLambda, bool isAsc, Expression<Func<T, TKey>> orderBy) where T : BaseModel, new()
        {
            currentPage = currentPage < 1 ? 1 : currentPage;
            pageSize = pageSize < 1 ? 20 : pageSize;
            var temp = _surpassDbContext.Set<T>().AsNoTracking().Where(whereLambda); //去掉.AsQueryable().AsNoTracking()，将下面改为

            var rest = new PageResponse<T>();
            rest.CurrentPage = currentPage;
            rest.PageSize = pageSize;
            rest.RecordTotal = await temp.CountAsync();//记录总条数时，自动设置了总页数
            if (isAsc)
            {
                rest.PageData = await temp.OrderBy(orderBy)
                     .Skip(pageSize * (currentPage - 1))
                     .Take(pageSize).ToListAsync(); //去掉.AsQueryable()，添加.select(t=>new Dto()).ToList()
            }

            rest.PageData = await temp.OrderByDescending(orderBy)
                .Skip(pageSize * (currentPage - 1))
                .Take(pageSize).ToListAsync(); //.select(t=>new Dto()).ToList()

            return rest;
        }

        public async Task<PageResponse<T>> LoadPageEntitiesAsync<T, TKey>(IQueryable<T> query, int currentPage, int pageSize, bool isAsc, Expression<Func<T, TKey>> orderBy) where T : class, new()
        {
            currentPage = currentPage < 1 ? 1 : currentPage;
            pageSize = pageSize < 1 ? 20 : pageSize;
            var rest = new PageResponse<T>();
            IQueryable<T> temp = query.AsNoTracking();
            rest.PageSize = pageSize;
            rest.CurrentPage = currentPage;
            rest.RecordTotal = await temp.CountAsync();
            if (isAsc)
            {
                rest.PageData = await temp.OrderBy(orderBy)
                    .Skip(pageSize * (currentPage - 1))
                    .Take(pageSize).ToListAsync();
            }
            else
            {
                rest.PageData = await temp.OrderByDescending(orderBy)
                    .Skip(pageSize * (currentPage - 1))
                    .Take(pageSize).ToListAsync();
            }
            return rest;
        }

        public  Task<PageResponse<TResult>> LoadPageEntitiesProjectToAsync<T, TKey, TResult>(IQueryable<T> query, int currentPage, int pageSize, bool isAsc, Expression<Func<T, TKey>> orderBy)
            where T : class, new()
            where TResult : class, new()
        {
            return  null;
            //currentPage = currentPage < 1 ? 1 : currentPage;
            //pageSize = pageSize < 1 ? 20 : pageSize;
            //var rest = new PageResponse<TResult>();
            //IQueryable<T> temp = query.AsNoTracking();
            //rest.PageSize = pageSize;
            //rest.CurrentPage = currentPage;
            //rest.RecordTotal = await temp.CountAsync();
            //if (isAsc)
            //{
            //    rest.PageData = await temp.OrderBy(orderBy)
            //        .Skip(pageSize * (currentPage - 1))
            //        .Take(pageSize).ProjectTo<TResult>(MapperProvider).ToListAsync();
            //}
            //else
            //{
            //    rest.PageData = await temp.OrderByDescending(orderBy)
            //        .Skip(pageSize * (currentPage - 1))
            //        .Take(pageSize).ProjectTo<TResult>(MapperProvider).ToListAsync();
            //}
            //return rest;
        }

        public int SaveChanges()
        {
            try
            {
                return _surpassDbContext.SaveChanges();
            }
            catch (DbException ex)
            {
                throw new CustomSystemException($"数据库保存失败!{ex.Message}", ResponseCode.DbEx);
            }
            catch (Exception ex)
            {
                throw new CustomSystemException($"数据库保存失败!{ex.Message}", ResponseCode.DbEx);
            }
        }

        public async Task<int> SaveChangesAsync()
        {
            try
            {
                return await _surpassDbContext.SaveChangesAsync();
            }
            catch (DbException ex)
            {
                throw new CustomSystemException($"数据库保存失败!{ex.Message}", ResponseCode.DbEx);
            }
            catch (Exception ex)
            {
                throw new CustomSystemException($"数据库保存失败!{ex.Message}", ResponseCode.DbEx);
            }
        }

        public void Update<T>(T entity) where T : BaseModel, new()
        {
            _surpassDbContext.Entry(entity).State = EntityState.Modified;
        }

        public void Dispose()
        {
            this._surpassDbContext.Dispose();
            GC.SuppressFinalize(this);
        }
    }
}
